#region license
// KenEgozi.com - my blog engine
// This project is licensed under the MIT License
//
//Copyright (c) 2007, Ken Egozi
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:

//The above copyright notice and this permission notice shall be included in
//all copies or substantial portions of the Software.

//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//THE SOFTWARE.
#endregion

using System;
using Castle.MonoRail.Framework;
using System.Web;
using KenEgozi.Com.Domain.Aggregates.Common;
using KenEgozi.Com.Domain.Aggregates.Post;
using KenEgozi.Com.Domain.Repositories;
using KenEgozi.Com.Weblog.Application;
using KenEgozi.Com.Weblog.Services;
using NHibernate.Expression;
using Rhino.Commons;

namespace KenEgozi.Com.Weblog.Controllers
{
	/// <summary>
	/// Adding comments to a post
	/// </summary>
	[Layout("Admin")]
	public class CommentsController : SmartDispatcherController
	{
		#region IoC
		private readonly IRepository<Comment> commentsRepository;
		private readonly ICachedItemsService cache;
		public CommentsController(IRepository<Comment> commentsRepository, ICachedItemsService cache)
		{
			this.commentsRepository = commentsRepository;
			this.cache = cache;
		}
		#endregion

		/// <summary>
		/// Adding comments to a post
		/// </summary>
		/// <param name="comment">The new comment</param>
		/// <param name="commentPermalink">The post's permalink the comment is to be attahced to</param>
		/// <param name="captcha">The captcha chakange entered by the user</param>
		/// <param name="rememberMe">If true, the user's details should be stored on a cookie</param>
		/// <param name="async">If true, then an ajax response will take place, redirect to the post's page will occur otherwise</param>
		//[Rescue("NotFound")]
		public void Add([DataBind("comment", Validate=true, From=ParamStore.Form)]Comment comment, string commentPermalink, string captcha, bool rememberMe, bool async)
		{
			if (!Context.UrlReferrer.StartsWith(Request.Uri.GetLeftPart(UriPartial.Authority)))
			{
				CannotAddComment();
				return;
			}

			if (HasValidationError(comment))
			{
				CannotAddComment();
				return;
			}

			if (Preferences.UseCaptcha)
				AssertCaptchaValidation(captcha);

			Post post = Repository.Posts.FindOne(Expression.InsensitiveLike("Permalink", commentPermalink, MatchMode.Exact));

			if (post == null)
			{
				CannotAddComment();
				return;
			}
			comment.IpAddress = Context.UnderlyingContext.Request.UserHostAddress;
			comment.Post = post;

			if (IsSpam(comment))
			{
				Response.StatusCode=501;
				RenderText("Sorry, but I cannot accept that comment.");
				return;
			}
			comment.IsProbablyOk();

			if (HasValidationError(comment))
			{
				CannotAddComment();
				return;
			}

			StoreUserDetailsInCookieIfUserTicked(rememberMe, comment);

			comment.Content = 
				HttpUtility.HtmlEncode(comment.Content)
				.Replace("\r\n", "<br />")
				.Replace("\n", "<br />")
				.Replace("\r", "<br />");

			post.Comments.Add(comment);
			With.Transaction(delegate
			{
				Repository.Posts.Update(post);
			});

			if (async)
			{
				if (Preferences.AutoApproveComments)
				{
					PropertyBag["comment"] = comment;
					RenderView("/Comments/View");
				}
				else
					RenderView("/Comments/Pending");
			}
			else
			{
				if (!Preferences.AutoApproveComments)
					Flash["comment"] = comment;
				RedirectToReferrer();
			}
		}

		[Cache(HttpCacheability.NoCache)]
		public void ListByPost(int postId, bool isAsync)
		{
			if (!isAsync)
			{
				PropertyBag["posts"] = Repository.Posts.FindAll();

				PropertyBag["postId"] = postId;
			}
			else
			{
				CancelLayout();
				RenderView("commentsList");
			}
			PropertyBag["comments"] = Repository.Posts.Get(postId).Comments;
		}
		
		public void Delete(int[] ids)
		{
			if (Session["username"] != null)
			{
				With.Transaction(delegate
									{
										foreach (int commentId in ids)
										{
											Comment comment = commentsRepository.Get(commentId);

											commentsRepository.Delete(comment);
										}
									});
				Flash["message"] = "" + ids.Length + " Deleted";
			}
			else
			{
				Flash["message"] = "Not allowed";
			}
			RedirectToReferrer();
		}
		#region helper methods
		private void StoreUserDetailsInCookieIfUserTicked(bool rememberMe, Comment comment)
		{
			if (rememberMe)
			{
				HttpCookie cookie = new HttpCookie("commenterData");
				cookie.Values.Add("Name", HttpUtility.HtmlEncode(comment.Name));
				cookie.Values.Add("Email", HttpUtility.HtmlEncode(comment.Email));
				cookie.Values.Add("Homepage", HttpUtility.HtmlEncode(comment.Homepage));
				cookie.Expires = DateTime.Now.AddYears(10);
				Response.CreateCookie(cookie);
			}
		}

		private void AssertCaptchaValidation(string captcha)
		{
			string storedCaptcha = Session["captcha"] as string;
			if (string.IsNullOrEmpty(storedCaptcha))
			{
				CannotAddComment();
				return;
			}

			// getting captcha and clearing so it won't be used again
			Session.Remove("captcha");
			if (!storedCaptcha.Equals(captcha, StringComparison.InvariantCultureIgnoreCase))
			{
				CannotAddComment();
			}
		}

		private void CannotAddComment()
		{
			Response.StatusCode = 501;
			RenderText("Cannot add comment");
		}

		private bool IsSpam(Comment comment)
		{
			foreach (BadWord badWord in cache.GetBadWords())
			{
				if (IsMatch(comment.Content, badWord.Value))
					return true;
				if (IsMatch(comment.Email, badWord.Value))
					return true;
				if (IsMatch(comment.Homepage, badWord.Value))
					return true;
			}

			foreach (BadIP badIP in cache.GetBadIPs())
			{
				if (badIP.Value == comment.IpAddress)
					return true;
			}

			return false;
		}
		private static bool IsMatch(string input, string badWord)
		{
			if (string.IsNullOrEmpty(input))
				return false;
			return (input.IndexOf(badWord, StringComparison.InvariantCultureIgnoreCase) > -1);
		}

		#endregion
	}
}
